package com.hyxt.utils;

import com.hyxt.DTO.T809_MessageHeader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;

/**
 * @Project CNPC_VMS
 * @Title module_name
 * @Description TODO
 * @author guanhl
 * @date 2014年8月7日 上午9:01:44
 * @company Beijing Huayou Information andCommunication Technology Co.,Ltd
 * @Copyright Copyright (c) 2014
 * @version V2.0
 */
public class T809_Util {

	private static Log log = LogFactory.getLog(T809_Util.class);
	// 拼接处理类名称
	private final static String HEX_NAME_L4 = "T809_0x";
	private final static String HEX_NAME_L3 = "T809_0x0";
	private final static String HEX_NAME_L2 = "T809_0x00";
	private final static String HEX_NAME_L1 = "T809_0x000";

	/**
	 * @Description 将时间转换成byte数组
	 * @param hour
	 *            ,minute,second
	 * @return byte数组
	 * @throws Exception
	 */
	public static byte[] time2byte(String hour, String minute, String second) {

		byte[] timebyte = new byte[4];
		// 获取小时字段
		byte[] hourbytes = Integer2HexBytes(Integer.parseInt(hour), 4);
		// 获取分钟字段
		byte[] minutebytes = Integer2HexBytes(Integer.parseInt(minute), 4);
		// 获取秒字段
		byte[] secondbytes = Integer2HexBytes(Integer.parseInt(second), 4);

		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		try {
			// 将是时分秒参数写入数据流
			bos.write(hourbytes[3]);
			bos.write(minutebytes[3]);
			bos.write(secondbytes[3]);
			timebyte = bos.toByteArray();
			// 关闭数据流
			bos.close();
		} catch (IOException e) {
			log.warn("", e);
		}

		return timebyte;
	}

	/**
	 * @Description 将时间类型转换成byte数组
	 * @param year
	 *            ，month，day
	 * @return byte数组
	 * @throws Exception
	 */
	public static byte[] date2byte(String year, String month, String day) {
		byte[] datebytes = new byte[4];
		// 获取天字段
		byte[] daybytes = Integer2HexBytes(Integer.parseInt(day), 4);
		// 获取月字段
		byte[] monthbytes = Integer2HexBytes(Integer.parseInt(month), 4);

		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		// 获取年字段
		byte[] yearbytes = Integer2HexBytes(Integer.parseInt(year), 4);

		try {
			// 将各个字段写入数据流
			bos.write(daybytes[3]);
			bos.write(monthbytes[3]);
			bos.write(yearbytes[2]);
			bos.write(yearbytes[3]);
			datebytes = bos.toByteArray();
			// 关闭流
			bos.close();
		} catch (IOException e) {
			log.warn("", e);
		}

		return datebytes;
	}

	/**
	 * @Description 将int类型数据转换成bytes数组
	 * @param src
	 *            ，bit
	 * @return byte数组
	 * @throws Exception
	 */
	public static byte[] Integer2HexBytes(int src, int bit) {
		byte[] datas = new byte[4];
		// 移位
		for (int i = (bit - 1); i >= 0; i--) {
			datas[(bit - 1) - i] = (byte) ((src >> 8 * i) & 0xff);
		}
		return datas;
	}

	/**
	 * @Description 数据加密算法
	 * @param key
	 *            ，buffer
	 * @return byte数组
	 * @throws Exception
	 */

	public static byte[] encrypt(int key, byte[] buffer, int M1, int IA1, int IC1) {
		int size = buffer.length;
		long lkey = key;
		int idx = 0;
		// 如果加密字段等于0，设置成1
		if (0 == key)
			key = 1;

		int mKey = M1;
		if (0 == mKey)
			mKey = 1;
		// 如果数据的长度大于0
		while (idx < size) {
			// 将秘钥转成byte数组
			byte[] bkey = BytesUtil.int2bytes4(key);
			long templkey = getUnsignedLong(bkey, 0);
			int temp = (int) (templkey % mKey) & 0xFFFFFFFF;
			lkey = (long) ((IA1 * temp + IC1));
			key = (int) lkey;
			// 移位
			buffer[idx] = (byte) (((byte) buffer[idx]) ^ ((byte) ((key >> 20) & 0xFF)));
			idx++;
		}
		return buffer;
	}

	/**
	 * @Description 获取无符号long类型
	 * @param hour
	 *            ,minute,second
	 * @return byte数组
	 * @throws Exception
	 */
	private static long getUnsignedLong(byte buf[], int index) {
		int firstByte = (0x000000FF & ((int) buf[index]));
		int secondByte = (0x000000FF & ((int) buf[index + 1]));
		int thirdByte = (0x000000FF & ((int) buf[index + 2]));
		int fourthByte = (0x000000FF & ((int) buf[index + 3]));
		long unsignedLong = ((long) (firstByte | secondByte << 8 | thirdByte << 16 | fourthByte << 24)) & 0xFFFFFFFFL;
		return unsignedLong;
	}

	/**
	 * @Description 获取数据头
	 * @param data
	 * @return byte数组
	 * @throws Exception
	 */
	public static int getMessageID(byte[] data, int[] codes) {
		// 截取消息id
		byte[] id = BytesUtil.getWord(9, data);
		// 创建新的byte数组，以便不改变原data
		byte[] edata = new byte[data.length];
		System.arraycopy(data, 0, edata, 0, data.length);
		int mid = BytesUtil.parseBytesToInt(id);
		// 判断数据体是否加密
		if (BytesUtil.getByte(18, data) == 1) {
			// 取出秘钥
			int key = BytesUtil.parseBytesToInt(BytesUtil.cutBytes(19, 4, data));
			// 数据头+crc校验码+头标识+尾标识=26个字节，用总长度减去26，剩余部分为body长度
			byte[] body = BytesUtil.cutBytes(23, data.length - 26, data);

			byte[] ebody = encrypt(key, body, codes[0], codes[1], codes[2]);

			System.arraycopy(ebody, 0, edata, 23, ebody.length);
		}
		// 判断父消息id
		byte[] cid = null;
		switch (mid) {
		// 主链路信息动态交互 归属
		case 0x1200:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 从链路信息动态交互 归属
		case 0x9200:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 主链路平台信息交互消息
		case 0x1300:
			cid = BytesUtil.getWord(23, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 从链路平台信息交互消息
		case 0x9300:
			cid = BytesUtil.getWord(23, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 主链路报警信息交互消息
		case 0x1400:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 从链路报警信息交互消息
		case 0x9400:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 主链路车辆监管业务
		case 0x1500:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 从链路车辆监管业务
		case 0x9500:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 主链路静态信息交换消息
		case 0x1600:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		// 从链路静态信息交换消息
		case 0x9600:
			cid = BytesUtil.getWord(45, edata);
			mid = BytesUtil.parseBytesToInt(cid);
			break;
		}
		// 返回子消息id
		return mid;
	}

	/**
	 * 转义
	 * 
	 * @param data
	 * @return
	 */
	public static byte[] escapeData(byte[] data) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		byte[] escapedDatabytes;
		for (int i = 0; i < data.length; i++) {
			// 如果数据中存在5b字段
			if (data[i] == 0x5b) {
				// 转换成5a01
				bos.write((byte) 0x5a);
				bos.write((byte) 0x01);
			} else if (data[i] == 0x5a) {
				// 如果里面有5a字段，就转换为5a02
				bos.write((byte) 0x5a);
				bos.write((byte) 0x02);
			} else if (data[i] == 0x5d) {
				// 如果里面有5d字段，就转换为5e01
				bos.write((byte) 0x5e);
				bos.write((byte) 0x01);
			} else if (data[i] == 0x5e) {
				// 如果里面有5e字段，就转换为5e02
				bos.write((byte) 0x5e);
				bos.write((byte) 0x02);
			} else {
				bos.write(data[i]);
			}
		}
		// 转义后的byte数组
		escapedDatabytes = bos.toByteArray();
		try {
			// 关闭流
			bos.close();
		} catch (IOException e) {
			log.warn("", e);
		}
		return escapedDatabytes;
	}

	/**
	 * @Description a) 若数据内容中有出现字符0x5b的，需替换为字符0x5a紧跟字符0x01; b)
	 *              若数据内容中有出现字符0x5a的，需替换为字符0x5a紧跟字符0x02; c)
	 *              若数据内容中有出现字符0x5d的，需替换为字符0x5e紧跟字符0x01; d)
	 *              若数据内容中有出现字符0x5e的，需替换为字符0x5e紧跟字符0x02.
	 * @param :args
	 * @return
	 * @throws ProtocolEscapeExeption
	 * @throws Exception
	 */
	public static byte[] reverseEscapeData(byte[] data) throws ProtocolEscapeExeption {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		byte[] reverseEscapedDatabytes = null;
		// 如果数据头为5b
		if (data[0] == 0x5b) {
			bos.write(0x5b);
		} else {
			// 如果不是5b，关闭流
			closeBos(bos);
			throw new ProtocolEscapeExeption(ByteUtil.bytes2BCDStr(data));
		}

		for (int i = 1; i < data.length - 1; i++) {
			switch (data[i]) {
			// 判断5a后面的数据，
			case 0x5a:
				// 进行反转义
				if (data[i + 1] == 0x01) {
					bos.write(0x5b);
					i++;
					break;
					// 反转义
				} else if (data[i + 1] == 0x02) {
					bos.write(0x5a);
					i++;
					break;
				}
				bos.write(data[i]);
				break;
			// 关闭流，抛出异常
			// closeBos(bos);
			// throw new ProtocolEscapeExeption(ByteUtil.bytes2BCDStr(data));
			// 判断5e后面的数据，
			case 0x5e:
				if (data[i + 1] == 0x01) {
					// 进行反转义
					bos.write(0x5d);
					i++;
					break;
				} else if (data[i + 1] == 0x02) {
					// 反转义
					bos.write(0x5e);
					i++;
					break;
				}

				bos.write(data[i]);
				// 关闭流，抛出异常
				// closeBos(bos);
				// throw new
				// ProtocolEscapeExeption(ByteUtil.bytes2BCDStr(data));
			default:
				bos.write(data[i]);
			}
		}
		// 判断尾标识
		if (data[data.length - 1] == 0x5d) {
			bos.write(0x5d);
		} else {
			// 关闭流
			closeBos(bos);
			throw new ProtocolEscapeExeption(ByteUtil.bytes2BCDStr(data));
		}
		// 获取反转义后的数据
		reverseEscapedDatabytes = bos.toByteArray();
		closeBos(bos);
		return reverseEscapedDatabytes;
	}

	/**
	 * @Description 关闭流
	 * @param OutputStream
	 * @return void
	 * @throws Exception
	 */
	private static void closeBos(OutputStream os) {
		try {
			os.close();
		} catch (IOException e) {
			log.warn("", e);
		}
	}

	/**
	 * @Description 检查crc校验码方法
	 * @param data
	 * @return void
	 * @throws Exception
	 */
	public static byte[] check(byte[] data) {

		byte[] checked = new byte[2];

		int crc = 0xFFFF;// 初始值
		short polynomial = 0x1021;// 0001 0000 0010 0001 (0,5,12)

		for (int j = 1; j < data.length - 3; ++j) {
			for (int i = 0; i < 8; i++) {
				// 移位操作
				boolean bit = ((data[j] >> (7 - i) & 1) == 1);
				boolean c15 = ((crc >> 15 & 1) == 1);
				crc <<= 1;
				// 算出crc码
				if (c15 ^ bit) {
					crc ^= polynomial;
				}
			}
		}
		crc &= 0xFFFF;
		checked = BytesUtil.int2bytes2(crc);
		return checked;
	}

	// MSG LENGTH Unit32_t 数据长度(包括头标识、数据头、数据体和尾标识)
	// MSG_SN Unit32_t 报文序列号a
	// MSG_ID Unit16_t 业务数据类型
	// MSG_GNSSCENTERID Unit32_t 下级平台接入码，上级平台给下级平台分配唯一标识码。
	// VERSION_FLAG BYTES 协议版本好标识，上下级平台之间采用的标准协议版编号；长度为3个字节来表示，0x01 0x02 0x0F
	// 标识的版本号是v1.2.15，以此类推。
	// ENCRYPT_FLAG BYTE 报文加密标识位b: 0表示报文不加密，1表示报文加密。
	//
	// ENCRYPT_KEY Unit32_t 数据加密的密匙，长度为4个字节。
	/**
	 * @Description 获取数据头
	 * @param data
	 * @return void
	 * @throws Exception
	 */
	public static T809_MessageHeader getHeader(byte[] reverseBytes) {

		T809_MessageHeader head = new T809_MessageHeader();
		// 截取数据长度
		byte[] lengthData = BytesUtil.getDWord(1, reverseBytes);
		int length = BytesUtil.parseBytesToInt(lengthData);
		// 截取鉴权码
		byte[] snData = BytesUtil.getDWord(5, reverseBytes);
		int sn = BytesUtil.parseBytesToInt(snData);
		byte[] idData = BytesUtil.getWord(9, reverseBytes);
		// 截取流水号
		int id = BytesUtil.parseBytesToInt(idData);
		byte[] gnsscenteridData = BytesUtil.getDWord(11, reverseBytes);
		// 截取接入码
		int gnsscenterid = BytesUtil.parseBytesToInt(gnsscenteridData);
		// 截取版本号
		byte[] versonData = BytesUtil.cutBytes(15, 3, reverseBytes);
		byte encrypt = BytesUtil.getByte(18, reverseBytes);
		byte[] encryptKeyData = BytesUtil.getDWord(19, reverseBytes);
		// 截取秘钥
		int encryptKey = BytesUtil.parseBytesToInt(encryptKeyData);
		head.setLength(length);
		head.setMessageID(id);
		head.setSn(sn);
		// 设置参数
		head.setGnsscentrid(gnsscenterid);
		head.setVersonFlag(versonData);
		head.setEncryptFlag(encrypt);
		head.setEncryptKey(encryptKey);

		return head;
	}

	/**
	 * @Description 十六进制变成大写字符串
	 * @param data
	 * @return void
	 * @throws Exception
	 */
	public static String hexToUpperCaseString(int num) {

		String numStr = Integer.toHexString(num).toUpperCase();
		int length = numStr.length();
		if (length > 4) {
			log.warn("the ID doesn't match the length less 4 !");
		}
		// 选择长度
		switch (length) {
		case 1:
			// 等于1时拼接字符串1
			numStr = HEX_NAME_L1 + numStr;
			break;
		// 等于2时拼接字符串2
		case 2:
			numStr = HEX_NAME_L2 + numStr;
			break;
		// 等于3时拼接字符串3
		case 3:
			numStr = HEX_NAME_L3 + numStr;
			break;
		// 等于4时拼接字符串4
		case 4:
			numStr = HEX_NAME_L4 + numStr;
			break;
		default:
			numStr = null;
		}
		return numStr;
	}

	/**
	 * @Description 图片转成byte数组方法
	 * @param String
	 * @return byte[]
	 * @throws Exception
	 */
	public static byte[] imageToByteArray(String imgPath) {
		BufferedInputStream in;
		try {
			// 将图片写入数据流
			in = new BufferedInputStream(new FileInputStream(imgPath));
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			int size = 0;
			byte[] temp = new byte[1024];
			while ((size = in.read(temp)) != -1) {
				// 遍历转换
				out.write(temp, 0, size);
			}
			// 关闭数据流
			in.close();
			// 返回byte数组
			return out.toByteArray();
		} catch (IOException e) {
			log.warn("", e);
			return null;
		}
	}

}
